import cv2
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False


def imageSpecification():
    img0 = cv2.imread("cat.JPG")
    img0 = cv2.cvtColor(img0, cv2.COLOR_RGB2GRAY)  # 装换成灰度图
    img1=img0.copy()
    h, w = img1.shape
    size = img1.size

    #构造目标图像，目标图像的每个灰度值得像素数量等于该灰度值
    tes2 = np.ones((1, 256 * 128), dtype='uint8')
    ss = 1
    m = 0
    while m < 255 * 128:
        for j in range(m - ss + 1, m + 1):
            tes2[0, j] = ss
        ss += 1
        m += ss

    #定义6个列表作为哈希表，前3个存储原图的映射关系，后3个存储目标图像映射关系
    map1 = []
    map2 = []
    map3 = []
    desMap1 = []
    desMap2 = []
    desMap3 = []

    for i in range(0, 256):
        desMap1.append(i)

    for i in range(0, 256):
        map1.append(0)

    # 得到原始图像的灰度级数量
    for i in range(h):
        for j in range(w):
            map1[img1[i, j]] += 1;


    # 计算原图灰度累计直方图
    map2.append(map1[0] / size)
    for i in range(0, 255):
        map2.append(map2[i] + map1[i + 1] / size)  # 递推公式：map2[i]=map2[i-1]+map1[i]/size
    for i in range(0, 256):
        map3.append(round(255 * map2[i]))


    # 目标图像的灰度级数量
    for i in range(tes2.shape[0]):
        for j in range(tes2.shape[1]):
            desMap1[tes2[i, j]] += 1

    # 计算灰度累计直方图
    desMap2.append(desMap1[0] / tes2.size)
    for k in range(255):
        desMap2.append(desMap2[k] + desMap1[k + 1] / tes2.size)
    for i in range(0, 256):
        desMap3.append(round(255 * desMap2[i]))

    # 进行规定化
    g = []
    for i in range(256):
        a = map3[i]
        f = True
        for j in range(256):
            if desMap3[j] == a:
                g.append(j)
                f = False
                break
        if f == True:
            minp = 255
            jmin = 0
            for j in range(256):
                b = abs(desMap3[j] - a)
                if b < minp:
                    minp = b
                    jmin = j
            g.append(jmin)

    for i in range(h):
        for j in range(w):
            img1[i, j] = g[img1[i, j]]
    return [img0,img1]

#在下面这个函数中调用imageSpecification函数，进行显示
def testImageSpecification():
    img0, img1 = imageSpecification()
    fig = plt.figure(num=2, figsize=(8, 5))
    plt.subplot(1, 2, 1)
    plt.title("原始图像直方图")
    plt.hist(img0.ravel(), 256, [0, 256])
    plt.xlim([0, 256])
    plt.subplot(1, 2, 2)
    plt.title("目标图像直方图")
    plt.hist(img1.ravel(), 256, [0, 256], color='#FF489A')
    plt.xlim([0, 256])
    plt.show()
    cv2.imshow("original image", img0)
    cv2.imshow("target image", img1)
    cv2.waitKey(0)


def conv(image, kernel, mode='same'):
    if mode == 'fill':  # 选择是否进行边缘填充
        h = kernel.shape[0] // 2  # 卷积核的列整除2
        w = kernel.shape[1] // 2  # 卷积核的行整除2
        # 在原始图像边缘进行填充，常数填充，填数值0
        image = np.pad(image, ((h, h), (w, w), (0, 0)), 'constant')

    # 进行卷积运算
    conv_b = convolve(image[:, :, 0], kernel)
    conv_g = convolve(image[:, :, 1], kernel)
    conv_r = convolve(image[:, :, 2], kernel)
    res = np.dstack([conv_b, conv_g, conv_r])
    return res


def convolve(image, kernel):
    h_kernel, w_kernel = kernel.shape  # 获取卷积核的长宽，也就是行数和列数

    h_image, w_image = image.shape  # 获取欲处理图片的长宽

    # 计算卷积核中心点开始运动的点
    res_h = h_image - h_kernel + 1
    res_w = w_image - w_kernel + 1

    # 生成一个numpy数组，用于保存处理后的图片
    res = np.zeros((res_h, res_w), np.uint8)

    for i in range(res_h):
        for j in range(res_w):
            # image处传入的是一个与卷积核一样大小矩阵，这个矩阵取自于欲处理图片的一部分
            # 这个矩阵与卷核进行运算，用i与j来进行卷积核滑动
            res[i, j] = getMultiplyAns(image[i:i + h_kernel, j:j + w_kernel], kernel)

    return res


# 两个数组(矩阵)，点对点相乘后进行累加
def getMultiplyAns(image, kernel):
    res = np.multiply(image, kernel).sum()
    if res > 255:
        return 255
    elif res < 0:
        return 0
    else:
        return res


# 测试函数
def testCovolve():
    image = cv2.imread("lena_noise.bmp")
    k1 = np.array([
        [1 / 9, 1 / 9, 1 / 9],
        [1 / 9, 1 / 9, 1 / 9],
        [1 / 9, 1 / 9, 1 / 9]
    ])
    k2 = np.array([[-1, 0, 1],
                   [-2, 0, 2],
                   [-1, 0, 1]])
    res1 = conv(image, k1, 'fill')
    cv2.imshow("Convoluted picture by k1", res1)
    res2 = conv(image, k2, 'fill')
    cv2.imshow("Convoluted picture by k2", res2)
    cv2.imshow('yuan shi tu xiang', image)
    cv2.waitKey(0)


if __name__ == '__main__':
    testImageSpecification()
    testCovolve()
